interface TreeConfigOptions {
  /** 子属性的名称，默认为 'children' */
  childProps?: string
}

/**
 * 遍历树形结构，并返回所有节点中指定的值。
 * @param tree - 树形结构数组
 * @param getValue - 获取节点值的函数
 * @param options - 配置选项，指定子节点数组的属性名，默认为 'children'
 * @returns 返回包含所有节点中指定值的数组
 */
const traverseTreeValues = <T, V>(tree: T[], getValue: (node: T) => V, options?: TreeConfigOptions): V[] => {
  const result: V[] = []
  const { childProps = 'children' } = options || {}

  const dfs = (treeNode: T) => {
    const value = getValue(treeNode)
    result.push(value)
    const children = (treeNode as Record<string, any>)?.[childProps]
    if (!children) return

    if (children.length > 0) {
      for (const child of children) {
        dfs(child)
      }
    }
  }

  for (const treeNode of tree) {
    dfs(treeNode)
  }

  return result.filter(Boolean)
}

/**
 * 根据条件过滤给定树结构的节点，并以原有顺序返回所有匹配节点的数组。
 * @param tree - 要过滤的树结构根节点数组
 * @param filter - 用于匹配每个节点的条件函数
 * @param options - 配置选项，指定子节点数组的属性名，默认为 'children'
 * @returns 包含所有匹配节点的数组
 */
const filterTree = <T extends Record<string, any>>(tree: T[], filter: (node: T) => boolean, options?: TreeConfigOptions): T[] => {
  const { childProps = 'children' } = options || {}

  const _filterTree = (nodes: T[]): T[] =>
    nodes.filter((node: any) => {
      if (filter(node as T)) {
        if (node[childProps]) {
          node[childProps] = _filterTree(node[childProps])
        }
        return true
      }
      return false
    })

  return _filterTree(tree)
}

/**
 * 根据条件重新映射给定树结构的节点
 * @param tree - 要映射的树结构的根节点数组
 * @param mapper - 用于映射每个节点的函数
 * @param options - 配置选项，指定子节点数组的属性名，默认为 'children'
 * @returns 返回映射后的树形结构数组
 */
const mapTree = <T, V extends Record<string, any>>(tree: T[], mapper: (node: T) => V, options?: TreeConfigOptions): V[] => {
  const { childProps = 'children' } = options || {}

  return tree.map((node) => {
    const mappedNode: Record<string, any> = mapper(node)
    if (mappedNode[childProps]) {
      mappedNode[childProps] = mapTree(mappedNode[childProps], mapper, options)
    }
    return mappedNode as V
  })
}

export { filterTree, mapTree, traverseTreeValues }
